package com.mach.platform.service.js.iot;

import com.google.common.base.Splitter;
import com.mach.platform.service.js.JsService;
import com.mach.platform.util.PairModel;
import com.mach.platform.util.SqlHelper;
import com.mach.platform.util.U;
import com.mach.platform.utils.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.script.ScriptEngine;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.sql.SQLException;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.util.*;

/*
  IOTVirJsService 是js虚拟仪表引擎，用来执行虚拟仪表里的计算功式的
 */
@Slf4j
@Service
public class IOTVirDevService implements  Runnable,Map<String,Object>{

    private DecimalFormat numFormat = new DecimalFormat("0.00");

    @Autowired
    private DataSource dataSource;
    private SqlHelper sqlHelper = null;

    private HashMap<String,Object> props = new HashMap<String,Object>();
    private Map<Object,Map<String,Object>> devsInObj;
    private ScriptEngine js = null;
    private SqlHelperJS sqlHelperJS;
    private long intter = 0;

    @PostConstruct
    public void startVirService(){

        this.js = JsService.get().newNashornEng();

        this.sqlHelper = SqlHelper.getBySource(this.dataSource);
        U.cachedThreadPool.execute(this);

        this.sqlHelperJS =  SqlHelperJS.getBySource(this.dataSource, this.js);

        this.js.put("db", this.sqlHelperJS);
        this.js.put("$", this);
    }

    private Random random = new Random();

    public Double rand(){
        Double r = 0.0;


        r = random.nextDouble();

        return r;
    }

    public List<String> doSplit(String target, String flag) {
        List<String> result = Splitter.on(flag).splitToList(target);
        return result;
    }
    public boolean ifEmpty(String target) {
        if ("undefined".equals(target)) {
            return false;
        }
        return true;
    }

    public long currentTimeMillis(){
        return System.currentTimeMillis();
    }

    public long addInr(){
        return ++this.intter;
    }

    public void flushAndReload(){
        props.clear();

    }

    public void calData(String startDateStr, String endDateStr){

        try {
            Date startDate = StrUtil.DFhh.parse(startDateStr);
            Date endDate = StrUtil.DFhh.parse(endDateStr);

            long startTime = startDate.getTime();
            long endTime = endDate.getTime();

            for(long i = startTime; i <= endTime; i+=(1000l * 60l * 60l) ){
                Date curDate = new Date(i);
                this.pick(StrUtil.DFhh.format(curDate),false);
            }

        } catch (ParseException e) {
            e.printStackTrace();
        }
    }


    @Override
    public void run() {
        while(true){

            try {
                this.pick();
                Thread.sleep(1*1000);
            } catch (Exception e) {
//                e.printStackTrace();
            }
        }
    }

    private StringBuilder intitDevValueObjJs(String theTime,boolean isNow) {
        StringBuilder jsSB = new StringBuilder();

        String devPreJs = "var deta = function(){};\n var runstate = function(){};\n var state = function(){};";
        try {
            InputStream stm = SimpleModelDev.class
                    .getResourceAsStream("/preVirDev.js");
            devPreJs = StrUtil.getCtxByStm(stm, 1024, "utf-8");
            stm.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        jsSB.append(devPreJs);

        if(!isNow){
            jsSB.append("var theTime='"+theTime+"';");
        }else{
            jsSB.append("var theTime=false;");
        }


//        String sql2GetDevValues = "select dev.id devId, \n" +
//                " dev.sn sn,\n" +
//                " coll.data_type collName,coll.dvalue collDvalue, \n" +
//                " '' from iot_dev_cur_data_win coll\n" +
//                " LEFT JOIN iot_dev dev on dev.id=coll.dev_id\n" +
//                " where 1=1 " +
//                " and dev.id is not null \n" +
////                " and dev.id in (161)" + // debug
//                " order by devId,collName ";

        String sql2GetDevValues = "";

        if(isNow){
            sql2GetDevValues = "select a.devId,a.sn,a.collName,a.collDvalue,a.collUnit,a.theTime\n" +
                    "from \n" +
                    "(\n" +
                    "select dev.id devId,dev.sn sn,coll.name collName,   ifnull(curd.dvalue, 0.0) collDvalue,coll.unit collUnit,curd.uptime as theTime\n" +
                    "\n" +
                    "from iot_dev dev\n" +
                    "\n" +
                    "LEFT JOIN iot_dev_type dtype on dtype.id=dev.dev_type_id\n" +
                    "LEFT JOIN iot_coll_items coll on dtype.id=coll.dev_type_id\n" +
                    "LEFT JOIN iot_dev_cur_data_win curd on dev.id = curd.dev_id and curd.data_type=coll.name\n" +
//                    " and DATE_FORMAT(curd.uptime,'%Y-%m-%d %H:%i') = '" + theTime  + "'\n" +
                    "where 1=1\n" +
                    "and coll.is_get = 1\n" +
                    "UNION\n" +
                    "select dev.id devId,dev.sn sn,coll.name collName,   ifnull(curd.dvalue, 0.0) collDvalue,coll.unit collUnit,curd.uptime as theTime\n" +
                    "from iot_dev dev\n" +
                    "LEFT JOIN iot_virtual_item coll on dev.id=coll.dev_id and dev.type=1\n" +
                    "LEFT JOIN iot_dev_cur_data_win curd on dev.id = curd.dev_id and curd.data_type=coll.name\n" +
//                    " and DATE_FORMAT(curd.uptime,'%Y-%m-%d %H:%i') = '" + theTime  + "'\n" +
                    "where 1=1\n" +
                    "and coll.is_get = 1\n" +
                    ") a\n" +
                    "order by devId";
        }else{
            sql2GetDevValues = "select a.devId,a.sn,a.collName,a.collDvalue,a.collUnit,a.theTime\n" +
                    "from \n" +
                    "(\n" +
                    "select dev.id devId,dev.sn sn,coll.name collName, ifnull(curd.dvalue, 0.0) collDvalue," +
                    "coll.unit collUnit,curd.the_time as theTime\n" +
                    "\n" +
                    "from iot_dev dev\n" +
                    "\n" +
                    "LEFT JOIN iot_dev_type dtype on dtype.id=dev.dev_type_id\n" +
                    "LEFT JOIN iot_coll_items coll on dtype.id=coll.dev_type_id\n" +
                    "LEFT JOIN iot_dev_data_win curd on dev.id = curd.dev_id and curd.data_type=coll.name and curd.the_time = STR_TO_DATE('"+theTime+"','%Y-%m-%d %H' )\n" +
                    "where 1=1\n" +
                    "and coll.is_get = 1\n" +
//                    "and dev_id = 10344\n" +
                    "UNION\n" +
                    "select dev.id devId,dev.sn sn,coll.name collName, ifnull(curd.dvalue, 0.0) collDvalue," +
                    "coll.unit collUnit,curd.the_time as theTime\n" +
                    "from iot_dev dev\n" +
                    "LEFT JOIN iot_virtual_item coll on dev.id=coll.dev_id and dev.type=1\n" +

                    "LEFT JOIN iot_dev_data_win curd on dev.id = curd.dev_id and curd.data_type=coll.name and curd.the_time = STR_TO_DATE('"+theTime+"','%Y-%m-%d %H' )\n" +
                    "where 1=1\n" +
                    "and coll.is_get = 1\n" +
//                    "and dev_id = 10344\n" +
                    ") a\n" +
                    "order by devId";
        }

        try {


            this.devsInObj =
                    sqlHelper.queryPairModel(sql2GetDevValues,
                            new PairModel("dev", "devId","coll",true));

//            log.info("devSize="+ dbObj.size());

            Set<Entry<Object, Map<String, Object>>> allDevEntrys = devsInObj.entrySet();



            for(Entry<Object, Map<String, Object>> enery:  allDevEntrys){
                try {
                    Object key = enery.getKey();
                    Map<String, Object> devInfoMap = enery.getValue();


                    Map<Integer, Object> collMap = (Map<Integer, Object>) devInfoMap.get("coll");

                    String sn = (String)devInfoMap.get("sn");

                    if(sn == null)continue;

                    Integer devId = (Integer)devInfoMap.get("devId");

                    if (sn != null && !("".equals(sn)) ) {
                        jsSB.append("var ").append(sn).append("={};\n");
                    }



                    //开始处理字段
                    Set<Entry<Integer, Object>> collEntrys = collMap.entrySet();

                    for(Entry<Integer, Object> collEntry : collEntrys){
                        Map<String, Object> coll = (Map<String, Object>) collEntry.getValue();



                        String collName = (String)coll.get("collName");
                        Object collDvalue = coll.get("collDvalue");

                        Object result = null;

                        try {

//                            if(!isNow && collDvalue == null){
//                                Map<String, Object> valueMap = sqlHelper.queryOne("select curd.dvalue dValue" +
//                                        " from iot_dev_data_win where dev_id=" + devId + " and dat_type='" + collName + "' " +
//                                        " and curd.the_time=STR_TO_DATE('" + theTime + ":00" + "','%Y-%m-%d %H:%i:%s')  ");
//
//                                collDvalue = valueMap.get("dValue");
//                            }

                            //TODO 设置每项数据
                            if (sn != null && !("".equals(sn)) && collName != null && collDvalue != null) {
                                jsSB.append(sn).append(".").append(collName).append("=").append(numFormat.format(collDvalue)).append(";");
                            }
                            if(sn != null && ("J009".equals(sn))){
                                log.debug("TEST sn=" + sn + " " + collName + " " + collDvalue);
                            }

                        }catch (Exception e){
                            e.printStackTrace();
//                            System.err.println("error msg:" + e.getLocalizedMessage() + " devId=" + devId + "");
                            log.error("error msg:" + e.getLocalizedMessage() + " devId=" + devId + "");
                            result = null;
                        }
                    }

                } catch (Exception e) {
                    e.printStackTrace();
                }
            }


        } catch (SQLException e) {
            e.printStackTrace();
        }

        return jsSB;

    }

    private void pick(){
//        this.pick(StrUtil.DFhh.format(new Date()), true);
    }

//    @Scheduled(cron = "0 10,20,30,40 * * * ?")
//    public void calLastHourData(){
////        log.info("开始计算上一个小时数据！");
////        this.pick(StrUtil.DFhh.format(new Date(System.currentTimeMillis() - 3600*1000)), false);
//    }

    private void pick(String theTime,boolean isNow) {

        //取值脚本
        StringBuilder inttValueJs = this.intitDevValueObjJs(theTime,isNow);

        //执行虚拟脚本代码
        String sql2GetDevs = "select dev.id devId, dev.name name,\n" +
                " dev.comm_addr commAddr,dev.sn sn,\n" +
                " coll.formula collFormula,  \n" +
                " coll.id collId, coll.name collName," +
                " coll.unit collUnit,coll.is_log collIsLog,\n" +
                " '' from iot_dev dev\n" +
                " LEFT JOIN iot_virtual_item coll on dev.id=coll.dev_id\n" +
                " where 1=1 \n" +
//                " and dev.id in (161)" + // debug
                " and dev.type in (1,2) " + // 虚拟设备
                " and coll.is_get=1 " +
//                " and dev_id = 10344 " +
                " order by devId,collId ";
        try {


             this.devsInObj =
                    sqlHelper.queryPairModel(sql2GetDevs,
                            new PairModel("dev", "devId","coll",true));

//            log.info("devSize="+ dbObj.size());

            Set<Entry<Object, Map<String, Object>>> allDevEntrys = devsInObj.entrySet();

            for(Entry<Object, Map<String, Object>> enery:  allDevEntrys){
                try {
                    Object key = enery.getKey();
                    Map<String, Object> devInfoMap = enery.getValue();

                    String prol = (String)devInfoMap.get("prol");

                    Map<Integer, Object> collMap = (Map<Integer, Object>) devInfoMap.get("coll");

                    Integer devId = (Integer)devInfoMap.get("devId");

                    String sn = (String)devInfoMap.get("sn");

                    //jsSB.append("var ").append(sn).append("={};");


                    //开始处理字段
                    Set<Entry<Integer, Object>> collEntrys = collMap.entrySet();

                    for(Entry<Integer, Object> collEntry : collEntrys){
                        Map<String, Object> coll = (Map<String, Object>) collEntry.getValue();
                        StringBuilder inttValueJsHasCollInfo = new StringBuilder();
                        inttValueJsHasCollInfo.append(inttValueJs);


                        String name = (String)devInfoMap.get("name");
                        String collName = (String)coll.get("collName");
                        String collUnit = (String)coll.get("collUnit");
                        String formula = (String)coll.get("collFormula");
                        Integer collIsLog = (Integer)coll.get("collIsLog");

                        Object result = null;

                        String script = "";
                        try {

                            //TODO 设置每项数据
                            if(formula == null || "".equals(formula))continue;
                            script = inttValueJsHasCollInfo.append("\n").append(formula).toString();
                            result = js.eval(script);
                            if("NaN".equals(result.toString())){
                                //log.info("script" + script);
                                result = null;
                            }

                        }catch (Exception e){
                            //e.printStackTrace();
                            log.error("error msg:" + e.getLocalizedMessage() + " devId=" + devId + "");
                            result = null;
                        }

                         if(result != null && (collIsLog !=null) && (collIsLog == 1) ){
                            this.addResult(devInfoMap, coll, result, theTime, isNow);
                        }
                        if(result != null && (collIsLog !=null) && (collIsLog == 0) && isNow){
                            this.addResultJustCur(devInfoMap, coll, result);
                        }
                    }

                } catch (Exception e) {
                    e.printStackTrace();
                }
            }


        } catch (SQLException e) {
            e.printStackTrace();
        }
    }


    public  void addResultJustCur(Map<String, Object> devInfoMap,Map<String, Object> coll,Object result){
        Integer devId = (Integer)devInfoMap.get("devId");
        String collName = (String)coll.get("collName");
        String collUnit = (String)coll.get("collUnit");
        try {

            sqlHelper.updateAsync(
                    "replace into iot_dev_cur_data_win" +
                            "(dev_id, data_type,dvalue,unit,uptime)" +
                            " value("+devId+", '"+collName+"', "+result+",'"+collUnit+"',now())");

            //this.dataLogService.addDataLogAsync(devId, new Date(), collName, result);

        } catch (Exception e) {
            e.printStackTrace();
            System.err.println("error saveToDb... msg:" + e.getLocalizedMessage() + " devId=" + devId +  "dataType=" + collName);
        }
    }


    public  void addResultJustWin(Map<String, Object> devInfoMap,Map<String, Object> coll,Object result,String theTime){
        Integer devId = (Integer)devInfoMap.get("devId");
        String collName = (String)coll.get("collName");
//        String collUnit = (String)coll.get("collUnit");
        try {

//            sqlHelper.updateAsync(
//                    "replace into iot_dev_data_win_bak" +
//                            "(order_num,dev_id, the_time,data_type,dvalue)" +
//                            " value(1,"+devId+",STR_TO_DATE(DATE_FORMAT(now(),'%Y-%m-%d %H:%i'),'%Y-%m-%d %H:%i'), '"+collName+"', "+result+")");
//            // 插入数据到 内存表，三个小时数据
//            sqlHelper.updateAsync(
//                    "replace into iot_dev_data_win" +
//                            "(order_num,dev_id, the_time,data_type,dvalue)" +
//                            " value(1,"+devId+",STR_TO_DATE(DATE_FORMAT(now(),'%Y-%m-%d %H:%i'),'%Y-%m-%d %H:%i'), '"+collName+"', "+result+")");


            sqlHelper.updateAsync(
                    "replace into iot_dev_data_win_bak" +
                            "(order_num,dev_id, the_time,data_type,dvalue)" +
                            " value(1,"+devId+",STR_TO_DATE('"+theTime+"','%Y-%m-%d %H'), '"+collName+"', "+result+")");
            // 插入数据到 内存表，三个小时数据
            sqlHelper.updateAsync(
                    "replace into iot_dev_data_win" +
                            "(order_num,dev_id, the_time,data_type,dvalue)" +
                            " value(1,"+devId+",STR_TO_DATE('"+theTime+"','%Y-%m-%d %H'), '"+collName+"', "+result+")");

            //this.dataLogService.addDataLogAsync(devId, new Date(), collName, result);

        } catch (Exception e) {
            e.printStackTrace();
            System.err.println("error saveToDb... msg:" + e.getLocalizedMessage() + " devId=" + devId +  "dataType=" + collName);
        }
    }

    public void addResult(Map<String, Object> devInfoMap,Map<String, Object> coll,Object result, String theTime, boolean isNow){

        try {
//            sqlHelper.updateAsync(
//                    "replace into iot_dev_cur_data_win" +
//                            "(dev_id, data_type,dvalue,unit,uptime)" +
//                            " value("+devId+", '"+collName+"', "+result+",'"+collUnit+"',now())");
//
//            sqlHelper.updateAsync(
//                    "insert into iot_dev_data_win" +
//                            "(order_num,dev_id, the_time,data_type,dvalue)" +
//                            " value(1,"+devId+",now(), '"+collName+"', "+result+")");

            //this.dataLogService.addDataLogAsync(devId, new Date(), collName, result);

            if(isNow)addResultJustCur(devInfoMap, coll, result);
            addResultJustWin(devInfoMap, coll, result, theTime);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void breakPoint(Map<Object,Object> obj){
        System.out.print("breakPoing obj=" + obj.toString());
    }



    @Override
    public int size() {
            return this.props.size();
            }

    @Override
    public boolean isEmpty() {
            return this.props.isEmpty();
            }

    @Override
    public boolean containsKey(Object key) {
            return this.props.containsValue(key);
            }

    @Override
    public boolean containsValue(Object value) {
            return this.props.containsValue(value);
            }

    @Override
    public Object get(Object key) {
            return this.props.get(key);
            }

    @Override
    public Object put(String key, Object value) {
            return this.props.put(key,value);
            }

    @Override
    public Object remove(Object key) {
            return this.props.remove(key);
            }

    @Override
    public void putAll(Map<? extends String, ?> m) {
            this.props.putAll(m);
            }

    @Override
    public void clear() {
            this.props.clear();
            }

    @Override
    public Set<String> keySet() {
            return this.props.keySet();
            }

    @Override
    public Collection<Object> values() {
            return this.props.values();
            }

    @Override
    public Set<Entry<String, Object>> entrySet() {
            return this.props.entrySet();
            }
}
